package com.srolorz.fmtp.thread.pool.executor.pool;

import com.srolorz.fmtp.thread.pool.executor.factory.AbstractThreadFactory;
import com.srolorz.fmtp.thread.pool.executor.factory.DefaultThreadFactory;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolManageExecutor {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolManageExecutor.class);
    private static volatile Map<String, ThreadPoolExecutor> threadPoolMap = new ConcurrentHashMap<>();
    private static ThreadPoolManageExecutor threadPoolManageExecutor;

    public ThreadPoolExecutor getThreadPoolExecutor(String threadGroupName) {
        if (StringUtils.isNotBlank(threadGroupName)) {
            ThreadPoolExecutor executor = threadPoolMap.get(threadGroupName);
            if (null != executor) {
                LOGGER.info("Obtain thread pool group success! threadGroup is [{}]", threadGroupName);
                return executor;
            }
        }

        int maxCount = 10;
        int count = 0;
        ThreadPoolExecutor threadPoolExecutor = null;
        String groupName = null;
        Iterator<Map.Entry<String, ThreadPoolExecutor>> iterator = threadPoolMap.entrySet().iterator();

        while (true) {
            ThreadPoolExecutor value;
            int activeCount;
            do {
                if (!iterator.hasNext()) {
                    return threadPoolExecutor;
                }
                Map.Entry<String, ThreadPoolExecutor> entry = iterator.next();
                value = entry.getValue();
                activeCount = value.getActiveCount();
                if (activeCount <= maxCount) {
                    groupName = entry.getKey().toString();
                    LOGGER.info("There is not set tread pool group which named {}", threadGroupName);
                    LOGGER.info("obtain default thread pool group - [{}], activeThreadNumber = {}", groupName, activeCount);
                    return value;
                }
            } while (count != 0 && count <= activeCount);

            count = activeCount;
            threadPoolExecutor = value;
        }

    }

    public static ThreadPoolManageExecutor getNewInstance() {
        threadPoolManageExecutor = ThreadPoolFactory.NEW_INSTANCE;
        return threadPoolManageExecutor;
    }

    public void addThreadPool(ThreadPoolManageEntity entity) {
        this.initThreadPool(entity);
    }

    private void initThreadPool(ThreadPoolManageEntity entity) {
        synchronized (this) {
            if (!this.checkInitParams(entity)) {
                AbstractThreadFactory abstractThreadFactory = DefaultThreadFactory.builder()
                        .setThreadGroup(entity.getThreadGroup())
                        .setThreadNamePrefix(entity.getThreadNamePrefix());
                AbstractThreadFactory threadFactory = entity.getThreadFactory() == null ? abstractThreadFactory : entity.getThreadFactory();
                String threadGroupName = threadFactory.getThreadGroup().getName();
                LOGGER.info("start init thread pool! threadPoolGroup={}", threadGroupName);
                if (threadPoolMap.containsKey(threadGroupName)) {
                    LOGGER.info("threadGroup[{}] is already exist!", threadGroupName);
                    return;
                }

                BlockingQueue blockingQueue = entity.getWorkQueue() == null ? new LinkedBlockingDeque(100) : entity.getWorkQueue();
                RejectedExecutionHandler handler = entity.getHandler() == null ? DefaultRefuseThreadPoolHandler.build() : entity.getHandler();
                ThreadPoolExecutor threadPoolExecutor = this.getThreadPoolExecutor(entity.getCorePoolSize(), entity.getMaxPoolSize(),
                        entity.getKeepAliveTime(), entity.getTimeUnit(), blockingQueue, threadFactory, handler);
                threadPoolMap.put(threadFactory.getThreadGroup().getName(), threadPoolExecutor);
                LOGGER.info("init thread pool success!");
            }
        }
    }

    private boolean checkInitParams(ThreadPoolManageEntity entity) {
        boolean isBlank = false;
        int corePoolSize = entity.getCorePoolSize();
        int maxPoolSize = entity.getMaxPoolSize();
        long keepAliveTime = entity.getKeepAliveTime();
        TimeUnit timeUnit = entity.getTimeUnit();
        if (corePoolSize <= 0 || maxPoolSize <= 0 || keepAliveTime <= 0L || null == timeUnit) {
            isBlank = true;
            LOGGER.warn("thread pool init param wrong:");
            LOGGER.warn("corePoolSize---{}", corePoolSize);
            LOGGER.warn("maxPoolSize---{}", maxPoolSize);
            LOGGER.warn("keepAliveTime---{}", keepAliveTime);
            LOGGER.warn("timeUnit---{}", timeUnit == null ? null : timeUnit.name());
        }
        return isBlank;
    }

    private ThreadPoolExecutor getThreadPoolExecutor(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit unit,
                                                     BlockingQueue<Runnable> queue, ThreadFactory threadFactory,
                                                     RejectedExecutionHandler handler) {
        return new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, queue, threadFactory, handler);
    }

    private static final class ThreadPoolFactory {
        private static final ThreadPoolManageExecutor NEW_INSTANCE = new ThreadPoolManageExecutor();
    }
}


